package com.acme.bankapp.domain;

import java.io.Serializable;
import java.util.ArrayList;

import com.acme.bankapp.domain.accounts.Account;
import com.acme.bankapp.exceptions.ClientExistsException;
import com.acme.bankapp.service.listeners.AbstractClientRegistrationListener;
import com.acme.bankapp.service.listeners.ClientRegistrationListener;

/**
 * 
 * Holds Clients and provides access to them.
 *
 */
public class Bank implements Serializable {

	ArrayList<Client> clients;
	ArrayList<ClientRegistrationListener> clientRegistrationListeners; 

	/**
	 * Creates Bank with no clients and no listeners.
	 */
	public Bank() {
		clients = new ArrayList<Client>();
		clientRegistrationListeners = new ArrayList<ClientRegistrationListener>(); 
	}

	/**
	 * Creates Bank with no clients and a debug listener (optional).
	 */
	public Bank(boolean withDebugListener) {
		clients = new ArrayList<Client>();
		clientRegistrationListeners = new ArrayList<ClientRegistrationListener>();
		if (withDebugListener) {

			/**
			 * Debug output generator for add client actions.
			 */
			class DebugListener extends AbstractClientRegistrationListener {
				public void onClientAdded(Client client) {
					System.out.println("\n--- [Listener] Adding client:");
					System.out.println(client + " " + (new java.util.Date()).toString().substring(11, 20));
				}

				public void onClientNotAdded(String message) {
					System.out.println("\n--- [Listener] Adding client...");
					System.out.println("Error: " + message);
				}
			}

			clientRegistrationListeners.add(new DebugListener());

		}
	}

	/**
	 * Adds specified listener to the Bank.
	 * @param listener to be added.
	 */
	public void addClientRegistrationListener(
			ClientRegistrationListener listener) {
		clientRegistrationListeners.add(listener);
	}

	/**
	 * Adds the specified Client object with specified initial balance.
	 * Polls listeners.  
	 * @param client Client object to be added.
	 * @return client, if it was added.
	 * @throws ClientExistsException if client with specified name is already present. 
	 */
	public Client addClient(Client client, Account... accounts) throws ClientExistsException {
		if (getClientByName(client.getName()) == null) {
			if (accounts.length > 0) {
				client.setAccount(accounts[0]);
				accounts[0].setClient(client);
			}
			clients.add(client);

			for (ClientRegistrationListener listener : clientRegistrationListeners) {
				listener.onClientAdded(client);
			}

			return client;
		} else {
			for (ClientRegistrationListener listener : clientRegistrationListeners) {
				listener.onClientNotAdded("Client " + client.getName() + " already exists.");
			}
			throw new ClientExistsException("Client " + client.getName() + " already exists.");
		}		
	}

	/**
	 * Returns a reference to the internal list of clients.
	 * @return ArrayList(Client)
	 */
	public ArrayList<Client> getClients() {
		return clients;	
	}

	/**
	 * Selects a Client with the specified name from all Clients.
	 * @param clientName - Name of the desired client. 
	 * @return Client object if such a client is present; null otherwise.
	 */
	public Client getClientByName(String clientName) {
		for (Client client : clients) {
			if (client.getName() == clientName) {
				return client; 
			}
		}

		return null;
	}

}
